R Notebook Questions, refined:

Do more Marylanders donate to in-state or out-of-state candidates? Which out-of-state candidates received the most donations and the greatest amount of money in donations? What are the demographics of people who donate to Maryland races only? To out-of-state races only? (using voter data OR using census data to see if those donors are clustered in specific locations, and, if they are, what the makeups of those locations are) Among top Maryland donors, what professions donate the most money to senate campaigns? What’s the makeup of donations received by Hogan and Alsobrooks? What percentage of their overall donations were large amounts of money (to be defined, but > $1000, for example) vs small amounts? Which party tends to donate more to out-of-state races?

We refined our dataset further by deciding to focus on individual donations made to senate races by donors in Maryland. Using the FEC website, we downloaded a dataset that met those parameters. We cleaned the data by removing unnecessary columns and renaming the columns where needed. We decided to limit our dataset to donations made in 2024 to ensure that the donations were made after the primary and before the general election. We hoped this would limit the number of candidates receiving donations, since most donations will go toward one of two parties in a certain race.

We also cleaned the data-set, removing numerous columns that were N/A or that were unnecessary to our project. We also had to rename one column and then filter to only the report year of 2024 to limit the donations. To answer our first question and questions, we’ll need to separate the out-of-state candidates from in-state candidates, but this should be easy because we’ll just need to filter out the Hogan and Alsobrooks committees to isolate the out-of-state candidates receiving donations. To answer our third question, we’ll need to either call in demographic data from the census or identify specific locations with clusters of donations, and look up information about those locations using census.gov. Question #4 asks about the professions of donors. One obvious challenge here is that professions and titles are often spelled or worded differently, even if they describe the same job– in this dataset, there are 2,738 different professions listed. To try to combat this, we will use open refine to reduce the number of occupations with slight variations in spelling or wording. We will then limit the data set to the top 1000 donations to make the analysis more manageable. We can also make some broad observations about the data even without cleaning the professions– for example, it’s obvious that the most donations come from people who say they are “not employed” (45568) or “retired” (29424). The next highest number of donations is more than 20,000 donations less than the number of donations from retired people. There are 207 committees that received donations in our dataset. Originally, we thought that we would use open refine to sort the committees by candidate name, because we thought that most candidates would have multiple committees. However, we found that this was actually pretty uncommon, and we weren’t able to match any names using open refine, once we uploaded our csv. For question 5, we need to do some extra research to decide how to define large donations. One idea is to find the average or median donation, and use that number as the dividing line between small and large donations. Another idea is to see if there’s an agreed-upon definition by people who work on campaigns or political scientists. We have one extra question (#6) that we would like to answer, but it involves party-level analysis that we can’t do because we don’t have the political parties of the donors in this data, and we don’t have anything in this data that indicates the party of each candidate. To answer this question, we could join this data with data from Act Blue and Win Red to identify the parties of donors. We can also probably find a dataset with the committee names and parties for all senate candidates, and join that dataset with ours.

Turn off scientific notation

options(scipen=999)
library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     ── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(lubridate)
install.packages('tidycensus')no
Error: unexpected symbol in "install.packages('tidycensus')no"
library(tidycensus)
md_senate_contributions <- read_csv("data/md_senate_contributions.csv") |>


print(column_names)
New names:Warning: One or more parsing issues, call `problems()` on your data frame for details, e.g.:
  dat <- vroom(...)
  problems(dat)Rows: 115275 Columns: 79── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (44): committee_id, committee_name...2, report_type, line_number, transaction_id, entity_type, entity_type_desc, unused_co...
dbl  (11): report_year, image_number, file_number, contributor_zip, contribution_receipt_amount, contributor_aggregate_ytd, con...
lgl  (22): committee_name...9, recipient_committee_org_type, memo_code_full, candidate_id, candidate_name, candidate_first_name...
dttm  (2): contribution_receipt_date, load_date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

#clean data

cleaned_data <- md_senate_contributions |>

select( -unused_contbr_id, -committee_name...9, -recipient_committee_org_type, -contributor_suffix, -contributor_street_2, -contributor_id, -memo_code, -memo_code_full, -candidate_id, -candidate_name, -candidate_first_name, -candidate_last_name, -candidate_middle_name, -candidate_prefix, -candidate_suffix, -candidate_office, -candidate_office_full, -candidate_office_state, -candidate_office_state_full, -candidate_office_district, -conduit_committee_id, -conduit_committee_name, -conduit_committee_street1, -conduit_committee_street2, -conduit_committee_city, -conduit_committee_state, -conduit_committee_zip, -donor_committee_name, -national_committee_nonfederal_account, -election_type_full, -increased_limit, -is_individual) |>

rename(committee_name = `committee_name...2`) |>

filter(report_year == 2024)


colnames(cleaned_data)
 [1] "committee_id"                    "committee_name"                  "report_year"                    
 [4] "report_type"                     "image_number"                    "line_number"                    
 [7] "transaction_id"                  "file_number"                     "entity_type"                    
[10] "entity_type_desc"                "contributor_prefix"              "contributor_name"               
[13] "recipient_committee_type"        "recipient_committee_designation" "contributor_first_name"         
[16] "contributor_middle_name"         "contributor_last_name"           "contributor_street_1"           
[19] "contributor_city"                "contributor_state"               "contributor_zip"                
[22] "contributor_employer"            "contributor_occupation"          "receipt_type"                   
[25] "receipt_type_desc"               "receipt_type_full"               "contribution_receipt_date"      
[28] "contribution_receipt_amount"     "contributor_aggregate_ytd"       "election_type"                  
[31] "fec_election_type_desc"          "fec_election_year"               "amendment_indicator"            
[34] "amendment_indicator_desc"        "schedule_type_full"              "load_date"                      
[37] "original_sub_id"                 "back_reference_transaction_id"   "back_reference_schedule_name"   
[40] "filing_form"                     "link_id"                         "memo_text"                      
[43] "two_year_transaction_period"     "schedule_type"                   "sub_id"                         
[46] "pdf_url"                         "line_number_label"              
most_contributions <- cleaned_data |>
select(committee_name, contribution_receipt_amount) |>
group_by(committee_name) |>
summarize(total_contribution = sum(contribution_receipt_amount, na.rm = TRUE)) |>
arrange(desc(total_contribution)) 
cleaned_data |>
  
write_csv("data/cleaned_md_senate_contributions.csv")
data <- read_delim("data/ccl.txt", delim = "|", col_names = FALSE)
Rows: 8568 Columns: 7── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "|"
chr (4): X1, X4, X5, X6
dbl (3): X2, X3, X7
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
cleaned_data_ccl <- data |>
rename(candidate_id = X1,candidate_election_year = X2, fec_election_year = X3, committee_id = X4,committee_type = X5,committee_design = X6, linkage_id = X7)

#inner join the committee id and fec election year

joined_data <- inner_join(cleaned_data, cleaned_data_ccl, by = c("committee_id", "fec_election_year"))
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.

#insert the candidate data

candidate_data <- read_delim("data/candidate_data.txt", delim = "|", col_names = FALSE) |>
  select(X1, X2, X3, X5) |> 
 rename(candidate_id = X1, candidate_name = X2, candidate_party = X5)
Rows: 3824 Columns: 30── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "|"
chr  (7): X1, X2, X3, X5, X19, X20, X28
dbl (18): X4, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X26, X27, X29, X30
lgl  (5): X21, X22, X23, X24, X25
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
candidate_joined_data <- inner_join(joined_data, candidate_data, by =c("candidate_id"))

#include only these rows


final_data <- candidate_joined_data |>
select(committee_id, committee_name, report_year, entity_type_desc,contributor_prefix, contributor_name, contributor_first_name, contributor_last_name, contributor_middle_name, contributor_street_1,contributor_city, contributor_state, contributor_zip,contributor_employer, contributor_occupation, contribution_receipt_date, contribution_receipt_amount, contributor_aggregate_ytd,memo_text,pdf_url, candidate_id, candidate_election_year)

Q1: Do more Marylanders donate to in-state or out-of-state candidates?

final_data_states <- final_data |>
  mutate(state = substr(candidate_id, 3, 4))
state_contributions <- final_data_states |>
  group_by(state) |>
  summarise(
    contribution_count = n(),                
    total_contribution = sum(contribution_receipt_amount, na.rm = TRUE)  
  )
maryland_contrib <- state_contributions |>
  filter(state == "MD") |>
  summarise(total_maryland_contrib = sum(total_contribution, na.rm = TRUE))


other_states_contrib <- state_contributions |>
  filter(state != "MD") |>
  summarise(total_other_states_contrib = sum(total_contribution, na.rm = TRUE))


comparison <- data.frame(
  state = c("Maryland", "Other States"),
  total_contribution = c(maryland_contrib$total_maryland_contrib, other_states_contrib$total_other_states_contrib)
)

A: Montana was surprisingly the state with the most contributions — edging Maryland with 202 more contributions Maryland. Maryland received the highest total sum however, with $5,516,562.24. That was $4,074,226.07 more than the next highest state, which was Montana. The other states combined recieved $7,358,970, a little less than two million more than just the state of Maryland.

Q2: Which out-of-state candidates received the most donations and the greatest amount of money in donations?

final_data_no_md <- final_data_states |>
  filter(state != "MD")
contributions_summary <- final_data_no_md |>
  group_by(committee_name) |>
  summarise(
    num_contributions = n(), 
    total_contribution_sum = sum(contribution_receipt_amount, na.rm = TRUE) 
  ) |>
  arrange(committee_name)

A2: Jon Tester, Sherrod Brown and Ruben Gallego were the top three in that order for both contributions recieved and the sum of contributions. The similarity between the three of them is that they were all Democrats running in tightly contested races. Brown was the only to lose his race. Jon Tester also clearly recieved more than the other two — which explains why Montana received so many more out-of-state contributions than other states. Montanans for Tester received 3,679 more contributions and $238,017.96 more in total contributions compared to Friends of Sherrod Brown.

Q3: What are the demographics of people who donate to Maryland races only? To out-of-state races only?

Step 1: find people who donate to ONLY Maryland races.


maryland_only_donors <- final_data_states%>%
  group_by(contributor_name) %>%                # Group by donor name
  summarize(only_md = all(state == "MD")) %>%  # Check if all donations were to Maryland
  filter(only_md) %>%                           # Keep only those who donated exclusively to Maryland
  select(contributor_name)                      # Select contributor names

full_maryland_only_donors <- final_data_states %>%
   filter(contributor_name %in% maryland_only_donors$contributor_name)


zips_full_maryland_only_donors <- full_maryland_only_donors %>%
  group_by(contributor_name, contributor_zip) %>%  # Group by contributor name and ZIP code
  summarize(
    num_donations = n(),                # Count the number of donations
    total_donated = sum(contribution_receipt_amount)  # Calculate the total amount donated
  ) %>%
  arrange(desc(num_donations))  # Optionally sort by number of donations
`summarise()` has grouped output by 'contributor_name'. You can override using the `.groups` argument.
md_zip_summary <- zips_full_maryland_only_donors %>%
  group_by(contributor_zip) %>%                # Group by ZIP code
  summarize(
    num_donations = n(),                      # Total number of donations per ZIP code
    total_donated = sum(total_donated)       # Total donation amount per ZIP code
  ) %>%
  arrange(desc(num_donations)) 
all_zcta <- get_acs(
  geography = "zcta",
  variables = c(
 "B01003_001", 
     "B01002_001", #= "median_age",
 "B19013_001", # = "median_income",
  "B02001_002", # = "white_alone",
     "B02001_003", # = "black_alone",
"B02001_004", # = "native_american",
    "B02001_005", #= "asian_alone",
 "B02001_006", # = "hawaiian_pacific",
"B02001_007",  # = "other_race",
    "B02001_008"), # = "two_or_more_races"
  
  year = 2022,
  survey = "acs5",
  output = "wide"
)
Getting data from the 2018-2022 5-year ACS
all_zcta <- all_zcta %>%
  rename(
    total_population = B01003_001E,       # Total population
    median_age = B01002_001E,            # Median age
    median_income = B19013_001E,         # Median household income
    white_alone = B02001_002E,           # White alone
    black_alone = B02001_003E,           # Black alone
    native_american = B02001_004E,       # Native American alone
    asian_alone = B02001_005E,           # Asian alone
    hawaiian_pacific = B02001_006E,      # Hawaiian/Pacific Islander alone
    other_race = B02001_007E,            # Other race
    two_or_more_races = B02001_008E      # Two or more races
  )

zips_full_maryland_only_donors <- zips_full_maryland_only_donors %>%
  mutate(
    # Extract the first 5 digits (if ZIP+4 format is used)
    contributor_zip = substr(contributor_zip, 1, 5),
    # Ensure all ZIP codes are 5 digits (pad with leading zeroes if needed)
    contributor_zip = sprintf("%05s", contributor_zip)
  )

zips_full_maryland_only_donors <- zips_full_maryland_only_donors %>%
  mutate(contributor_zip = as.character(contributor_zip))

merged_data_md <- zips_full_maryland_only_donors %>%
  left_join(all_zcta, by = c("contributor_zip" = "GEOID"))

summarized_md_donor_demos_by_zip <- merged_data_md %>%
  group_by(contributor_zip) %>%
  summarize(
    total_population = first(total_population),  # Keeps the total population for each GEOID
    median_age = first(median_age),              # Keeps the median age
    median_income = first(median_income),        # Keeps the median income
    white_alone = first(white_alone),            # Keeps the white alone population
    black_alone = first(black_alone),            # Keeps the black alone population
    native_american = first(native_american),    # Keeps the native american population
    asian_alone = first(asian_alone),            # Keeps the asian alone population
    hawaiian_pacific = first(hawaiian_pacific),  # Keeps the hawaiian pacific population
    other_race = first(other_race),              # Keeps the other race population
    two_or_more_races = first(two_or_more_races),# Keeps the two or more races population
    num_donations = n(),                       # Counts the number of donations per GEOID
    total_amt_donated = sum(total_donated, na.rm = TRUE)  # Sums the donation amount
  ) |> 
  arrange(desc(num_donations))  |> 
  mutate(
    # Calculate per-capita (percentage) for each demographic group
    white_alone_per_capita = white_alone / total_population,
    black_alone_per_capita = black_alone / total_population,
    native_american_per_capita = native_american / total_population,
    asian_alone_per_capita = asian_alone / total_population,
    hawaiian_pacific_per_capita = hawaiian_pacific / total_population,
    other_race_per_capita = other_race / total_population,
    two_or_more_races_per_capita = two_or_more_races / total_population,
    median_income_per_capita = median_income / total_population
  ) |>
   mutate(
    # Convert per-capita values to percentages by multiplying by 100
    white_alone_percent = white_alone_per_capita * 100,
    black_alone_percent = black_alone_per_capita * 100,
    native_american_percent = native_american_per_capita * 100,
    asian_alone_percent = asian_alone_per_capita * 100,
    hawaiian_pacific_percent = hawaiian_pacific_per_capita * 100,
    other_race_percent = other_race_per_capita * 100,
    two_or_more_races_percent = two_or_more_races_per_capita * 100,
    
    # Median income per capita (optional), but normally income isn't per person
    median_income_per_capita = median_income_per_capita * 100  # Optional
  )

cleaner_md_donor_demos <- summarized_md_donor_demos_by_zip |> 
    select(-matches("_per_capita"), -"native_american", -"hawaiian_pacific", -"other_race", -"two_or_more_races", -"white_alone", -"black_alone", -"asian_alone")

#repeat for out of state donors

out_of_state_donors <- final_data_states |>
  filter(state != "MD")

xmaryland_donors <- final_data_states %>%
  filter(state == "MD")

out_of_state_only_donors <- out_of_state_donors %>%
  filter(!contributor_name %in% xmaryland_donors$contributor_name)  %>%
group_by(contributor_name, contributor_zip) %>%  # Group by contributor name and ZIP code
  summarize(
    num_donations = n(),                # Count the number of donations
    total_donated = sum(contribution_receipt_amount)  # Calculate the total amount donated
  ) %>%
  arrange(desc(num_donations))  # Opt
`summarise()` has grouped output by 'contributor_name'. You can override using the `.groups` argument.
out_of_state_only_donors <- out_of_state_only_donors %>%
  mutate(
    # Extract the first 5 digits (if ZIP+4 format is used)
    contributor_zip = substr(contributor_zip, 1, 5),
    # Ensure all ZIP codes are 5 digits (pad with leading zeroes if needed)
    contributor_zip = sprintf("%05s", contributor_zip)
  )

merged_data_oos <- out_of_state_only_donors %>%
  left_join(all_zcta, by = c("contributor_zip" = "GEOID"))

cleaner_oos_donor_demos <- merged_data_oos  %>%
   group_by(contributor_zip) %>%
  summarize(
    total_population = first(total_population),  # Keeps the total population for each GEOID
    median_age = first(median_age),              # Keeps the median age
    median_income = first(median_income),        # Keeps the median income
    white_alone = first(white_alone),            # Keeps the white alone population
    black_alone = first(black_alone),            # Keeps the black alone population
    native_american = first(native_american),    # Keeps the native american population
    asian_alone = first(asian_alone),            # Keeps the asian alone population
    hawaiian_pacific = first(hawaiian_pacific),  # Keeps the hawaiian pacific population
    other_race = first(other_race),              # Keeps the other race population
    two_or_more_races = first(two_or_more_races),# Keeps the two or more races population
    num_donations = n(),                       # Counts the number of donations per GEOID
    total_amt_donated = sum(total_donated, na.rm = TRUE)  # Sums the donation amount
  ) |> 
  arrange(desc(num_donations))  |> 
  mutate(
    # Calculate per-capita (percentage) for each demographic group
    white_alone_per_capita = white_alone / total_population,
    black_alone_per_capita = black_alone / total_population,
    native_american_per_capita = native_american / total_population,
    asian_alone_per_capita = asian_alone / total_population,
    hawaiian_pacific_per_capita = hawaiian_pacific / total_population,
    other_race_per_capita = other_race / total_population,
    two_or_more_races_per_capita = two_or_more_races / total_population,
    median_income_per_capita = median_income / total_population
  ) |>
   mutate(
    # Convert per-capita values to percentages by multiplying by 100
    white_alone_percent = white_alone_per_capita * 100,
    black_alone_percent = black_alone_per_capita * 100,
    native_american_percent = native_american_per_capita * 100,
    asian_alone_percent = asian_alone_per_capita * 100,
    hawaiian_pacific_percent = hawaiian_pacific_per_capita * 100,
    other_race_percent = other_race_per_capita * 100,
    two_or_more_races_percent = two_or_more_races_per_capita * 100,
    
    # Median income per capita (optional), but normally income isn't per person
    median_income_per_capita = median_income_per_capita * 100  # Optional
  ) |> select(-matches("_per_capita"), -"native_american", -"hawaiian_pacific", -"other_race", -"two_or_more_races", -"white_alone", -"black_alone", -"asian_alone")
cleaner_oos_donor_demos |> arrange(desc(total_amt_donated))
cleaner_md_donor_demos |> arrange(desc(total_amt_donated))

It looks like in this data, the top contributing zip codes are the same for out-of-state and in-state donations. The top out-of-state donors donated much more than the in-state donors, and there were more of them in the top zip codes. A top in-state donor zip code is Anapolis, and doesn’t appear in the top ten zip codes for out-of-state.

As we keep going to answer this question, we should calculate the amount donated per person. We should consider presenting this data with two maps side-by-side.

Question 4: Among top Maryland donors, what professions donate the most money to senate campaigns?

individual_donors  <- final_data_states %>%
group_by(contributor_name, contributor_zip, contributor_occupation, contributor_employer) %>%  # Group by contributor name and ZIP code
  summarize(
    num_donations = n(),                # Count the number of donations
    total_donated = sum(contribution_receipt_amount)  # Calculate the total amount donated
  ) %>%
  arrange(desc(num_donations))  # Opt
`summarise()` has grouped output by 'contributor_name', 'contributor_zip', 'contributor_occupation'. You can override using the `.groups` argument.
jobs_to_clean <- individual_donors |> 
  group_by(contributor_occupation) |>
  summarize(
    number_jobs = n(),
    num_donations = n(),  
     total_donations = sum(total_donated)
  ) |> arrange(desc(number_jobs)) |>
write_csv("jobs_to_clean.csv")
boss_to_clean  <- individual_donors |> 
  group_by(contributor_employer) |>
  summarize(
    number_jobs = n(),
    num_donations = n(),  
     total_donations = sum(total_donated)
  ) |> arrange(desc(number_jobs)) |>
write_csv("boss_to_clean.csv")
clean_employer <- read_csv("cleaned_boss.csv")
Rows: 4700 Columns: 6── Column specification ────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (3): contributor_employer, employer_categories, cleaned_boss
dbl (3): number_jobs, num_donations, total_donations
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
clean_occupation <- read_csv("clean_jobs.csv")
Rows: 1963 Columns: 5── Column specification ────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): contributor_occupation, cleaned_jjobs
dbl (3): number_jobs, num_donations, total_donations
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
clean_occupation |> 
  group_by(cleaned_jjobs)  |> 
  summarize(
    number_jobs = n(),
    num_donations = n(),  
     total_donated = sum(total_donations)) |> 
  arrange(desc(number_jobs))
NA
clean_occupation |> 
  group_by(cleaned_jjobs)  |> 
  summarize(
    number_jobs = n(),
    num_donations = n(),  
     total_donated = sum(total_donations)) |> 
  arrange(desc(total_donated))
NA
clean_employer |> 
  group_by(cleaned_boss) |>
  summarize(
    number_jobs = n(),
    num_donations = n(),  
     total_donated = sum(total_donations)
  ) |>
  arrange(desc(number_jobs))

To answer this question, I put a csv of occupations into OpenRefine. We originally planned to limit the data set, but I found that there were only 1,963 job titles in the data set, which seemed like a reasonable number to refine down into a smaller list of jobs.

To do this, I grouped certain jobs into categories like “director” and “executive” – so job titles like “sales director” went into the “director” category.

I think the OpenRefine method may be flawed/not tell us very much because I was only able to get the list down to 1,464 jobs after about 1 hour of refining. I also worry that the categorizing method I used obsures information that might be important – Director of Sales is different from Director of Education, for example, and these jobs were grouped into the same position. And there are such variations in job title that I was not able to categorize all job titles well.

What we can tell from this data is that the most frequently-held jobs among Maryland donors are leadership/management positions, so likely well-paid. The most money was donated by people who are not employed or retired.

We should consider repeating this question with information about employers. I’ve started to do this in OpenRefine, but this will take longer, because there are more than 7,000 different employers. However, many of these seem to be able to be reconciled in OpenRefine. And I’m interested to see how many donations/how much money was donated by people who work for top employers in Maryland, like Johns Hopkins.

#Question 5: What’s the makeup of donations received by Hogan and Alsobrooks? What percentage of their overall donations were large amounts of money (to be defined, but > $1000, for example) vs small amounts?

filtered_data <- final_data |>
  filter(committee_name %in% c("HOGAN FOR MARYLAND INC.", "ALSOBROOKS FOR SENATE"))
large_donation_threshold <- 1000
hogan_alsobrooks_filtered_data <- filtered_data |> mutate( donation_category = ifelse(contribution_receipt_amount > large_donation_threshold, "Large", "Small") )
category_totals <- hogan_alsobrooks_filtered_data |>
group_by(donation_category) %>% summarise( total_amount = sum(contribution_receipt_amount, na.rm = TRUE) )
category_totals <- category_totals %>% mutate( percentage = total_amount / sum(total_amount) * 100 )

To

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKUiBOb3RlYm9vawpRdWVzdGlvbnMsIHJlZmluZWQ6CgpEbyBtb3JlIE1hcnlsYW5kZXJzIGRvbmF0ZSB0byBpbi1zdGF0ZSBvciBvdXQtb2Ytc3RhdGUgY2FuZGlkYXRlcz8gV2hpY2ggb3V0LW9mLXN0YXRlIGNhbmRpZGF0ZXMgcmVjZWl2ZWQgdGhlIG1vc3QgZG9uYXRpb25zIGFuZCB0aGUgZ3JlYXRlc3QgYW1vdW50IG9mIG1vbmV5IGluIGRvbmF0aW9ucz8gV2hhdCBhcmUgdGhlIGRlbW9ncmFwaGljcyBvZiBwZW9wbGUgd2hvIGRvbmF0ZSB0byBNYXJ5bGFuZCByYWNlcyBvbmx5PyBUbyBvdXQtb2Ytc3RhdGUgcmFjZXMgb25seT8gKHVzaW5nIHZvdGVyIGRhdGEgT1IgdXNpbmcgY2Vuc3VzIGRhdGEgdG8gc2VlIGlmIHRob3NlIGRvbm9ycyBhcmUgY2x1c3RlcmVkIGluIHNwZWNpZmljIGxvY2F0aW9ucywgYW5kLCBpZiB0aGV5IGFyZSwgd2hhdCB0aGUgbWFrZXVwcyBvZiB0aG9zZSBsb2NhdGlvbnMgYXJlKSBBbW9uZyB0b3AgTWFyeWxhbmQgZG9ub3JzLCB3aGF0IHByb2Zlc3Npb25zIGRvbmF0ZSB0aGUgbW9zdCBtb25leSB0byBzZW5hdGUgY2FtcGFpZ25zPyBXaGF04oCZcyB0aGUgbWFrZXVwIG9mIGRvbmF0aW9ucyByZWNlaXZlZCBieSBIb2dhbiBhbmQgQWxzb2Jyb29rcz8gV2hhdCBwZXJjZW50YWdlIG9mIHRoZWlyIG92ZXJhbGwgZG9uYXRpb25zIHdlcmUgbGFyZ2UgYW1vdW50cyBvZiBtb25leSAodG8gYmUgZGVmaW5lZCwgYnV0ID4gJDEwMDAsIGZvciBleGFtcGxlKSB2cyBzbWFsbCBhbW91bnRzPyBXaGljaCBwYXJ0eSB0ZW5kcyB0byBkb25hdGUgbW9yZSB0byBvdXQtb2Ytc3RhdGUgcmFjZXM/CgpXZSByZWZpbmVkIG91ciBkYXRhc2V0IGZ1cnRoZXIgYnkgZGVjaWRpbmcgdG8gZm9jdXMgb24gaW5kaXZpZHVhbCBkb25hdGlvbnMgbWFkZSB0byBzZW5hdGUgcmFjZXMgYnkgZG9ub3JzIGluIE1hcnlsYW5kLiBVc2luZyB0aGUgRkVDIHdlYnNpdGUsIHdlIGRvd25sb2FkZWQgYSBkYXRhc2V0IHRoYXQgbWV0IHRob3NlIHBhcmFtZXRlcnMuIFdlIGNsZWFuZWQgdGhlIGRhdGEgYnkgcmVtb3ZpbmcgdW5uZWNlc3NhcnkgY29sdW1ucyBhbmQgcmVuYW1pbmcgdGhlIGNvbHVtbnMgd2hlcmUgbmVlZGVkLiBXZSBkZWNpZGVkIHRvIGxpbWl0IG91ciBkYXRhc2V0IHRvIGRvbmF0aW9ucyBtYWRlIGluIDIwMjQgdG8gZW5zdXJlIHRoYXQgdGhlIGRvbmF0aW9ucyB3ZXJlIG1hZGUgYWZ0ZXIgdGhlIHByaW1hcnkgYW5kIGJlZm9yZSB0aGUgZ2VuZXJhbCBlbGVjdGlvbi4gV2UgaG9wZWQgdGhpcyB3b3VsZCBsaW1pdCB0aGUgbnVtYmVyIG9mIGNhbmRpZGF0ZXMgcmVjZWl2aW5nIGRvbmF0aW9ucywgc2luY2UgbW9zdCBkb25hdGlvbnMgd2lsbCBnbyB0b3dhcmQgb25lIG9mIHR3byBwYXJ0aWVzIGluIGEgY2VydGFpbiByYWNlLgoKV2UgYWxzbyBjbGVhbmVkIHRoZSBkYXRhLXNldCwgcmVtb3ZpbmcgbnVtZXJvdXMgY29sdW1ucyB0aGF0IHdlcmUgTi9BIG9yIHRoYXQgd2VyZSB1bm5lY2Vzc2FyeSB0byBvdXIgcHJvamVjdC4gV2UgYWxzbyBoYWQgdG8gcmVuYW1lIG9uZSBjb2x1bW4gYW5kIHRoZW4gZmlsdGVyIHRvIG9ubHkgdGhlIHJlcG9ydCB5ZWFyIG9mIDIwMjQgdG8gbGltaXQgdGhlIGRvbmF0aW9ucy4gVG8gYW5zd2VyIG91ciBmaXJzdCBxdWVzdGlvbiBhbmQgcXVlc3Rpb25zLCB3ZeKAmWxsIG5lZWQgdG8gc2VwYXJhdGUgdGhlIG91dC1vZi1zdGF0ZSBjYW5kaWRhdGVzIGZyb20gaW4tc3RhdGUgY2FuZGlkYXRlcywgYnV0IHRoaXMgc2hvdWxkIGJlIGVhc3kgYmVjYXVzZSB3ZeKAmWxsIGp1c3QgbmVlZCB0byBmaWx0ZXIgb3V0IHRoZSBIb2dhbiBhbmQgQWxzb2Jyb29rcyBjb21taXR0ZWVzIHRvIGlzb2xhdGUgdGhlIG91dC1vZi1zdGF0ZSBjYW5kaWRhdGVzIHJlY2VpdmluZyBkb25hdGlvbnMuIFRvIGFuc3dlciBvdXIgdGhpcmQgcXVlc3Rpb24sIHdl4oCZbGwgbmVlZCB0byBlaXRoZXIgY2FsbCBpbiBkZW1vZ3JhcGhpYyBkYXRhIGZyb20gdGhlIGNlbnN1cyBvciBpZGVudGlmeSBzcGVjaWZpYyBsb2NhdGlvbnMgd2l0aCBjbHVzdGVycyBvZiBkb25hdGlvbnMsIGFuZCBsb29rIHVwIGluZm9ybWF0aW9uIGFib3V0IHRob3NlIGxvY2F0aW9ucyB1c2luZyBjZW5zdXMuZ292LiBRdWVzdGlvbiAjNCBhc2tzIGFib3V0IHRoZSBwcm9mZXNzaW9ucyBvZiBkb25vcnMuIE9uZSBvYnZpb3VzIGNoYWxsZW5nZSBoZXJlIGlzIHRoYXQgcHJvZmVzc2lvbnMgYW5kIHRpdGxlcyBhcmUgb2Z0ZW4gc3BlbGxlZCBvciB3b3JkZWQgZGlmZmVyZW50bHksIGV2ZW4gaWYgdGhleSBkZXNjcmliZSB0aGUgc2FtZSBqb2LigJMgaW4gdGhpcyBkYXRhc2V0LCB0aGVyZSBhcmUgMiw3MzggZGlmZmVyZW50IHByb2Zlc3Npb25zIGxpc3RlZC4gVG8gdHJ5IHRvIGNvbWJhdCB0aGlzLCB3ZSB3aWxsIHVzZSBvcGVuIHJlZmluZSB0byByZWR1Y2UgdGhlIG51bWJlciBvZiBvY2N1cGF0aW9ucyB3aXRoIHNsaWdodCB2YXJpYXRpb25zIGluIHNwZWxsaW5nIG9yIHdvcmRpbmcuIFdlIHdpbGwgdGhlbiBsaW1pdCB0aGUgZGF0YSBzZXQgdG8gdGhlIHRvcCAxMDAwIGRvbmF0aW9ucyB0byBtYWtlIHRoZSBhbmFseXNpcyBtb3JlIG1hbmFnZWFibGUuIFdlIGNhbiBhbHNvIG1ha2Ugc29tZSBicm9hZCBvYnNlcnZhdGlvbnMgYWJvdXQgdGhlIGRhdGEgZXZlbiB3aXRob3V0IGNsZWFuaW5nIHRoZSBwcm9mZXNzaW9uc+KAkyBmb3IgZXhhbXBsZSwgaXTigJlzIG9idmlvdXMgdGhhdCB0aGUgbW9zdCBkb25hdGlvbnMgY29tZSBmcm9tIHBlb3BsZSB3aG8gc2F5IHRoZXkgYXJlIOKAnG5vdCBlbXBsb3llZOKAnSAoNDU1NjgpIG9yIOKAnHJldGlyZWTigJ0gKDI5NDI0KS4gVGhlIG5leHQgaGlnaGVzdCBudW1iZXIgb2YgZG9uYXRpb25zIGlzIG1vcmUgdGhhbiAyMCwwMDAgZG9uYXRpb25zIGxlc3MgdGhhbiB0aGUgbnVtYmVyIG9mIGRvbmF0aW9ucyBmcm9tIHJldGlyZWQgcGVvcGxlLiBUaGVyZSBhcmUgMjA3IGNvbW1pdHRlZXMgdGhhdCByZWNlaXZlZCBkb25hdGlvbnMgaW4gb3VyIGRhdGFzZXQuIE9yaWdpbmFsbHksIHdlIHRob3VnaHQgdGhhdCB3ZSB3b3VsZCB1c2Ugb3BlbiByZWZpbmUgdG8gc29ydCB0aGUgY29tbWl0dGVlcyBieSBjYW5kaWRhdGUgbmFtZSwgYmVjYXVzZSB3ZSB0aG91Z2h0IHRoYXQgbW9zdCBjYW5kaWRhdGVzIHdvdWxkIGhhdmUgbXVsdGlwbGUgY29tbWl0dGVlcy4gSG93ZXZlciwgd2UgZm91bmQgdGhhdCB0aGlzIHdhcyBhY3R1YWxseSBwcmV0dHkgdW5jb21tb24sIGFuZCB3ZSB3ZXJlbuKAmXQgYWJsZSB0byBtYXRjaCBhbnkgbmFtZXMgdXNpbmcgb3BlbiByZWZpbmUsIG9uY2Ugd2UgdXBsb2FkZWQgb3VyIGNzdi4gRm9yIHF1ZXN0aW9uIDUsIHdlIG5lZWQgdG8gZG8gc29tZSBleHRyYSByZXNlYXJjaCB0byBkZWNpZGUgaG93IHRvIGRlZmluZSBsYXJnZSBkb25hdGlvbnMuIE9uZSBpZGVhIGlzIHRvIGZpbmQgdGhlIGF2ZXJhZ2Ugb3IgbWVkaWFuIGRvbmF0aW9uLCBhbmQgdXNlIHRoYXQgbnVtYmVyIGFzIHRoZSBkaXZpZGluZyBsaW5lIGJldHdlZW4gc21hbGwgYW5kIGxhcmdlIGRvbmF0aW9ucy4gQW5vdGhlciBpZGVhIGlzIHRvIHNlZSBpZiB0aGVyZeKAmXMgYW4gYWdyZWVkLXVwb24gZGVmaW5pdGlvbiBieSBwZW9wbGUgd2hvIHdvcmsgb24gY2FtcGFpZ25zIG9yIHBvbGl0aWNhbCBzY2llbnRpc3RzLiBXZSBoYXZlIG9uZSBleHRyYSBxdWVzdGlvbiAoIzYpIHRoYXQgd2Ugd291bGQgbGlrZSB0byBhbnN3ZXIsIGJ1dCBpdCBpbnZvbHZlcyBwYXJ0eS1sZXZlbCBhbmFseXNpcyB0aGF0IHdlIGNhbuKAmXQgZG8gYmVjYXVzZSB3ZSBkb27igJl0IGhhdmUgdGhlIHBvbGl0aWNhbCBwYXJ0aWVzIG9mIHRoZSBkb25vcnMgaW4gdGhpcyBkYXRhLCBhbmQgd2UgZG9u4oCZdCBoYXZlIGFueXRoaW5nIGluIHRoaXMgZGF0YSB0aGF0IGluZGljYXRlcyB0aGUgcGFydHkgb2YgZWFjaCBjYW5kaWRhdGUuIFRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uLCB3ZSBjb3VsZCBqb2luIHRoaXMgZGF0YSB3aXRoIGRhdGEgZnJvbSBBY3QgQmx1ZSBhbmQgV2luIFJlZCB0byBpZGVudGlmeSB0aGUgcGFydGllcyBvZiBkb25vcnMuIFdlIGNhbiBhbHNvIHByb2JhYmx5IGZpbmQgYSBkYXRhc2V0IHdpdGggdGhlIGNvbW1pdHRlZSBuYW1lcyBhbmQgcGFydGllcyBmb3IgYWxsIHNlbmF0ZSBjYW5kaWRhdGVzLCBhbmQgam9pbiB0aGF0IGRhdGFzZXQgd2l0aCBvdXJzLgoKIyBUdXJuIG9mZiBzY2llbnRpZmljIG5vdGF0aW9uCgpgYGB7cn0Kb3B0aW9ucyhzY2lwZW49OTk5KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCgoKYGBgCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygndGlkeWNlbnN1cycpbm8KYGBgCgpgYGB7cn0KbGlicmFyeSh0aWR5Y2Vuc3VzKQpgYGAKCgoKYGBge3J9Cm1kX3NlbmF0ZV9jb250cmlidXRpb25zIDwtIHJlYWRfY3N2KCJkYXRhL21kX3NlbmF0ZV9jb250cmlidXRpb25zLmNzdiIpIHw+CgoKcHJpbnQoY29sdW1uX25hbWVzKQpgYGAKCiNjbGVhbiBkYXRhCmBgYHtyfQpjbGVhbmVkX2RhdGEgPC0gbWRfc2VuYXRlX2NvbnRyaWJ1dGlvbnMgfD4KCnNlbGVjdCggLXVudXNlZF9jb250YnJfaWQsIC1jb21taXR0ZWVfbmFtZS4uLjksIC1yZWNpcGllbnRfY29tbWl0dGVlX29yZ190eXBlLCAtY29udHJpYnV0b3Jfc3VmZml4LCAtY29udHJpYnV0b3Jfc3RyZWV0XzIsIC1jb250cmlidXRvcl9pZCwgLW1lbW9fY29kZSwgLW1lbW9fY29kZV9mdWxsLCAtY2FuZGlkYXRlX2lkLCAtY2FuZGlkYXRlX25hbWUsIC1jYW5kaWRhdGVfZmlyc3RfbmFtZSwgLWNhbmRpZGF0ZV9sYXN0X25hbWUsIC1jYW5kaWRhdGVfbWlkZGxlX25hbWUsIC1jYW5kaWRhdGVfcHJlZml4LCAtY2FuZGlkYXRlX3N1ZmZpeCwgLWNhbmRpZGF0ZV9vZmZpY2UsIC1jYW5kaWRhdGVfb2ZmaWNlX2Z1bGwsIC1jYW5kaWRhdGVfb2ZmaWNlX3N0YXRlLCAtY2FuZGlkYXRlX29mZmljZV9zdGF0ZV9mdWxsLCAtY2FuZGlkYXRlX29mZmljZV9kaXN0cmljdCwgLWNvbmR1aXRfY29tbWl0dGVlX2lkLCAtY29uZHVpdF9jb21taXR0ZWVfbmFtZSwgLWNvbmR1aXRfY29tbWl0dGVlX3N0cmVldDEsIC1jb25kdWl0X2NvbW1pdHRlZV9zdHJlZXQyLCAtY29uZHVpdF9jb21taXR0ZWVfY2l0eSwgLWNvbmR1aXRfY29tbWl0dGVlX3N0YXRlLCAtY29uZHVpdF9jb21taXR0ZWVfemlwLCAtZG9ub3JfY29tbWl0dGVlX25hbWUsIC1uYXRpb25hbF9jb21taXR0ZWVfbm9uZmVkZXJhbF9hY2NvdW50LCAtZWxlY3Rpb25fdHlwZV9mdWxsLCAtaW5jcmVhc2VkX2xpbWl0LCAtaXNfaW5kaXZpZHVhbCkgfD4KCnJlbmFtZShjb21taXR0ZWVfbmFtZSA9IGBjb21taXR0ZWVfbmFtZS4uLjJgKSB8PgoKZmlsdGVyKHJlcG9ydF95ZWFyID09IDIwMjQpCgoKY29sbmFtZXMoY2xlYW5lZF9kYXRhKQpgYGAKCmBgYHtyfQptb3N0X2NvbnRyaWJ1dGlvbnMgPC0gY2xlYW5lZF9kYXRhIHw+CnNlbGVjdChjb21taXR0ZWVfbmFtZSwgY29udHJpYnV0aW9uX3JlY2VpcHRfYW1vdW50KSB8Pgpncm91cF9ieShjb21taXR0ZWVfbmFtZSkgfD4Kc3VtbWFyaXplKHRvdGFsX2NvbnRyaWJ1dGlvbiA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQsIG5hLnJtID0gVFJVRSkpIHw+CmFycmFuZ2UoZGVzYyh0b3RhbF9jb250cmlidXRpb24pKSAKY2xlYW5lZF9kYXRhIHw+CiAgCndyaXRlX2NzdigiZGF0YS9jbGVhbmVkX21kX3NlbmF0ZV9jb250cmlidXRpb25zLmNzdiIpCmRhdGEgPC0gcmVhZF9kZWxpbSgiZGF0YS9jY2wudHh0IiwgZGVsaW0gPSAifCIsIGNvbF9uYW1lcyA9IEZBTFNFKQoKY2xlYW5lZF9kYXRhX2NjbCA8LSBkYXRhIHw+CnJlbmFtZShjYW5kaWRhdGVfaWQgPSBYMSxjYW5kaWRhdGVfZWxlY3Rpb25feWVhciA9IFgyLCBmZWNfZWxlY3Rpb25feWVhciA9IFgzLCBjb21taXR0ZWVfaWQgPSBYNCxjb21taXR0ZWVfdHlwZSA9IFg1LGNvbW1pdHRlZV9kZXNpZ24gPSBYNiwgbGlua2FnZV9pZCA9IFg3KQpgYGAKCiNpbm5lciBqb2luIHRoZSBjb21taXR0ZWUgaWQgYW5kIGZlYyBlbGVjdGlvbiB5ZWFyIAoKYGBge3J9CmpvaW5lZF9kYXRhIDwtIGlubmVyX2pvaW4oY2xlYW5lZF9kYXRhLCBjbGVhbmVkX2RhdGFfY2NsLCBieSA9IGMoImNvbW1pdHRlZV9pZCIsICJmZWNfZWxlY3Rpb25feWVhciIpKQpgYGAKCiNpbnNlcnQgdGhlIGNhbmRpZGF0ZSBkYXRhCmBgYHtyfQpjYW5kaWRhdGVfZGF0YSA8LSByZWFkX2RlbGltKCJkYXRhL2NhbmRpZGF0ZV9kYXRhLnR4dCIsIGRlbGltID0gInwiLCBjb2xfbmFtZXMgPSBGQUxTRSkgfD4KICBzZWxlY3QoWDEsIFgyLCBYMywgWDUpIHw+IAogcmVuYW1lKGNhbmRpZGF0ZV9pZCA9IFgxLCBjYW5kaWRhdGVfbmFtZSA9IFgyLCBjYW5kaWRhdGVfcGFydHkgPSBYNSkKCmNhbmRpZGF0ZV9qb2luZWRfZGF0YSA8LSBpbm5lcl9qb2luKGpvaW5lZF9kYXRhLCBjYW5kaWRhdGVfZGF0YSwgYnkgPWMoImNhbmRpZGF0ZV9pZCIpKQpgYGAKCgojaW5jbHVkZSBvbmx5IHRoZXNlIHJvd3MgCgpgYGB7cn0KCmZpbmFsX2RhdGEgPC0gY2FuZGlkYXRlX2pvaW5lZF9kYXRhIHw+CnNlbGVjdChjb21taXR0ZWVfaWQsIGNvbW1pdHRlZV9uYW1lLCByZXBvcnRfeWVhciwgZW50aXR5X3R5cGVfZGVzYyxjb250cmlidXRvcl9wcmVmaXgsIGNvbnRyaWJ1dG9yX25hbWUsIGNvbnRyaWJ1dG9yX2ZpcnN0X25hbWUsIGNvbnRyaWJ1dG9yX2xhc3RfbmFtZSwgY29udHJpYnV0b3JfbWlkZGxlX25hbWUsIGNvbnRyaWJ1dG9yX3N0cmVldF8xLGNvbnRyaWJ1dG9yX2NpdHksIGNvbnRyaWJ1dG9yX3N0YXRlLCBjb250cmlidXRvcl96aXAsY29udHJpYnV0b3JfZW1wbG95ZXIsIGNvbnRyaWJ1dG9yX29jY3VwYXRpb24sIGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2RhdGUsIGNvbnRyaWJ1dGlvbl9yZWNlaXB0X2Ftb3VudCwgY29udHJpYnV0b3JfYWdncmVnYXRlX3l0ZCxtZW1vX3RleHQscGRmX3VybCwgY2FuZGlkYXRlX2lkLCBjYW5kaWRhdGVfZWxlY3Rpb25feWVhcikKYGBgCgpRMTogRG8gbW9yZSBNYXJ5bGFuZGVycyBkb25hdGUgdG8gaW4tc3RhdGUgb3Igb3V0LW9mLXN0YXRlIGNhbmRpZGF0ZXM/IAoKYGBge3J9CmZpbmFsX2RhdGFfc3RhdGVzIDwtIGZpbmFsX2RhdGEgfD4KICBtdXRhdGUoc3RhdGUgPSBzdWJzdHIoY2FuZGlkYXRlX2lkLCAzLCA0KSkKYGBgCgpgYGB7cn0Kc3RhdGVfY29udHJpYnV0aW9ucyA8LSBmaW5hbF9kYXRhX3N0YXRlcyB8PgogIGdyb3VwX2J5KHN0YXRlKSB8PgogIHN1bW1hcmlzZSgKICAgIGNvbnRyaWJ1dGlvbl9jb3VudCA9IG4oKSwgICAgICAgICAgICAgICAgCiAgICB0b3RhbF9jb250cmlidXRpb24gPSBzdW0oY29udHJpYnV0aW9uX3JlY2VpcHRfYW1vdW50LCBuYS5ybSA9IFRSVUUpICAKICApCmBgYAoKYGBge3J9Cm1hcnlsYW5kX2NvbnRyaWIgPC0gc3RhdGVfY29udHJpYnV0aW9ucyB8PgogIGZpbHRlcihzdGF0ZSA9PSAiTUQiKSB8PgogIHN1bW1hcmlzZSh0b3RhbF9tYXJ5bGFuZF9jb250cmliID0gc3VtKHRvdGFsX2NvbnRyaWJ1dGlvbiwgbmEucm0gPSBUUlVFKSkKCgpvdGhlcl9zdGF0ZXNfY29udHJpYiA8LSBzdGF0ZV9jb250cmlidXRpb25zIHw+CiAgZmlsdGVyKHN0YXRlICE9ICJNRCIpIHw+CiAgc3VtbWFyaXNlKHRvdGFsX290aGVyX3N0YXRlc19jb250cmliID0gc3VtKHRvdGFsX2NvbnRyaWJ1dGlvbiwgbmEucm0gPSBUUlVFKSkKCgpjb21wYXJpc29uIDwtIGRhdGEuZnJhbWUoCiAgc3RhdGUgPSBjKCJNYXJ5bGFuZCIsICJPdGhlciBTdGF0ZXMiKSwKICB0b3RhbF9jb250cmlidXRpb24gPSBjKG1hcnlsYW5kX2NvbnRyaWIkdG90YWxfbWFyeWxhbmRfY29udHJpYiwgb3RoZXJfc3RhdGVzX2NvbnRyaWIkdG90YWxfb3RoZXJfc3RhdGVzX2NvbnRyaWIpCikKYGBgCgoKQTogTW9udGFuYSB3YXMgc3VycHJpc2luZ2x5IHRoZSBzdGF0ZSB3aXRoIHRoZSBtb3N0IGNvbnRyaWJ1dGlvbnMgLS0tIGVkZ2luZyBNYXJ5bGFuZCB3aXRoIDIwMiBtb3JlIGNvbnRyaWJ1dGlvbnMgTWFyeWxhbmQuIE1hcnlsYW5kIHJlY2VpdmVkIHRoZSBoaWdoZXN0IHRvdGFsIHN1bSBob3dldmVyLCB3aXRoICQ1LDUxNiw1NjIuMjQuIFRoYXQgd2FzICQ0LDA3NCwyMjYuMDcgbW9yZSB0aGFuIHRoZSBuZXh0IGhpZ2hlc3Qgc3RhdGUsIHdoaWNoIHdhcyBNb250YW5hLiBUaGUgb3RoZXIgc3RhdGVzIGNvbWJpbmVkIHJlY2lldmVkICQ3LDM1OCw5NzAsIGEgbGl0dGxlIGxlc3MgdGhhbiB0d28gbWlsbGlvbiBtb3JlIHRoYW4ganVzdCB0aGUgc3RhdGUgb2YgTWFyeWxhbmQuIAoKClEyOiBXaGljaCBvdXQtb2Ytc3RhdGUgY2FuZGlkYXRlcyByZWNlaXZlZCB0aGUgbW9zdCBkb25hdGlvbnMgYW5kIHRoZSBncmVhdGVzdCBhbW91bnQgb2YgbW9uZXkgaW4gZG9uYXRpb25zPyAKCmBgYHtyfQpmaW5hbF9kYXRhX25vX21kIDwtIGZpbmFsX2RhdGFfc3RhdGVzIHw+CiAgZmlsdGVyKHN0YXRlICE9ICJNRCIpCmBgYAoKYGBge3J9CmNvbnRyaWJ1dGlvbnNfc3VtbWFyeSA8LSBmaW5hbF9kYXRhX25vX21kIHw+CiAgZ3JvdXBfYnkoY29tbWl0dGVlX25hbWUpIHw+CiAgc3VtbWFyaXNlKAogICAgbnVtX2NvbnRyaWJ1dGlvbnMgPSBuKCksIAogICAgdG90YWxfY29udHJpYnV0aW9uX3N1bSA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQsIG5hLnJtID0gVFJVRSkgCiAgKSB8PgogIGFycmFuZ2UoY29tbWl0dGVlX25hbWUpCmBgYAoKQTI6IEpvbiBUZXN0ZXIsIFNoZXJyb2QgQnJvd24gYW5kIFJ1YmVuIEdhbGxlZ28gd2VyZSB0aGUgdG9wIHRocmVlIGluIHRoYXQgb3JkZXIgZm9yIGJvdGggY29udHJpYnV0aW9ucyByZWNpZXZlZCBhbmQgdGhlIHN1bSBvZiBjb250cmlidXRpb25zLiBUaGUgc2ltaWxhcml0eSBiZXR3ZWVuIHRoZSB0aHJlZSBvZiB0aGVtIGlzIHRoYXQgdGhleSB3ZXJlIGFsbCBEZW1vY3JhdHMgcnVubmluZyBpbiB0aWdodGx5IGNvbnRlc3RlZCByYWNlcy4gQnJvd24gd2FzIHRoZSBvbmx5IHRvIGxvc2UgaGlzIHJhY2UuIEpvbiBUZXN0ZXIgYWxzbyBjbGVhcmx5IHJlY2lldmVkIG1vcmUgdGhhbiB0aGUgb3RoZXIgdHdvIC0tLSB3aGljaCBleHBsYWlucyB3aHkgTW9udGFuYSByZWNlaXZlZCBzbyBtYW55IG1vcmUgb3V0LW9mLXN0YXRlIGNvbnRyaWJ1dGlvbnMgdGhhbiBvdGhlciBzdGF0ZXMuIE1vbnRhbmFucyBmb3IgVGVzdGVyIHJlY2VpdmVkIDMsNjc5IG1vcmUgY29udHJpYnV0aW9ucyBhbmQgJDIzOCwwMTcuOTYgbW9yZSBpbiB0b3RhbCBjb250cmlidXRpb25zIGNvbXBhcmVkIHRvIEZyaWVuZHMgb2YgU2hlcnJvZCBCcm93bi4KClEzOiBXaGF0IGFyZSB0aGUgZGVtb2dyYXBoaWNzIG9mIHBlb3BsZSB3aG8gZG9uYXRlIHRvIE1hcnlsYW5kIHJhY2VzIG9ubHk/IFRvIG91dC1vZi1zdGF0ZSByYWNlcyBvbmx5PwoKU3RlcCAxOiBmaW5kIHBlb3BsZSB3aG8gZG9uYXRlIHRvIE9OTFkgTWFyeWxhbmQgcmFjZXMuCmBgYHtyfQoKbWFyeWxhbmRfb25seV9kb25vcnMgPC0gZmluYWxfZGF0YV9zdGF0ZXN8PgogIGdyb3VwX2J5KGNvbnRyaWJ1dG9yX25hbWUpIHw+ICAgICAgICAgICAgICAgIAogIHN1bW1hcml6ZShvbmx5X21kID0gYWxsKHN0YXRlID09ICJNRCIpKSB8PgogIGZpbHRlcihvbmx5X21kKSB8PiAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgc2VsZWN0KGNvbnRyaWJ1dG9yX25hbWUpICAgICAgICAgICAgICAgICAgICAKCmZ1bGxfbWFyeWxhbmRfb25seV9kb25vcnMgPC0gZmluYWxfZGF0YV9zdGF0ZXMgfD4KICAgZmlsdGVyKGNvbnRyaWJ1dG9yX25hbWUgJWluJSBtYXJ5bGFuZF9vbmx5X2Rvbm9ycyRjb250cmlidXRvcl9uYW1lKQoKCnppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyA8LSBmdWxsX21hcnlsYW5kX29ubHlfZG9ub3JzIHw+CiAgZ3JvdXBfYnkoY29udHJpYnV0b3JfbmFtZSwgY29udHJpYnV0b3JfemlwKSB8PgogIHN1bW1hcml6ZSgKICAgIG51bV9kb25hdGlvbnMgPSBuKCksICAgICAgICAgICAgIAogICAgdG90YWxfZG9uYXRlZCA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQpIAogICl8PgogIGFycmFuZ2UoZGVzYyhudW1fZG9uYXRpb25zKSkgIAoKbWRfemlwX3N1bW1hcnkgPC0gemlwc19mdWxsX21hcnlsYW5kX29ubHlfZG9ub3JzIHw+CiAgZ3JvdXBfYnkoY29udHJpYnV0b3JfemlwKSB8PiAgICAgICAgICAgICAgCiAgc3VtbWFyaXplKAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgCiAgICB0b3RhbF9kb25hdGVkID0gc3VtKHRvdGFsX2RvbmF0ZWQpICAgICAgCiAgKSB8PgogIGFycmFuZ2UoZGVzYyhudW1fZG9uYXRpb25zKSkgCgpgYGAKYGBge3J9CmFsbF96Y3RhIDwtIGdldF9hY3MoCiAgZ2VvZ3JhcGh5ID0gInpjdGEiLAogIHZhcmlhYmxlcyA9IGMoCiAiQjAxMDAzXzAwMSIsIAogICAgICJCMDEwMDJfMDAxIiwgIz0gIm1lZGlhbl9hZ2UiLAogIkIxOTAxM18wMDEiLCAjID0gIm1lZGlhbl9pbmNvbWUiLAogICJCMDIwMDFfMDAyIiwgIyA9ICJ3aGl0ZV9hbG9uZSIsCiAgICAgIkIwMjAwMV8wMDMiLCAjID0gImJsYWNrX2Fsb25lIiwKIkIwMjAwMV8wMDQiLCAjID0gIm5hdGl2ZV9hbWVyaWNhbiIsCiAgICAiQjAyMDAxXzAwNSIsICM9ICJhc2lhbl9hbG9uZSIsCiAiQjAyMDAxXzAwNiIsICMgPSAiaGF3YWlpYW5fcGFjaWZpYyIsCiJCMDIwMDFfMDA3IiwgICMgPSAib3RoZXJfcmFjZSIsCiAgICAiQjAyMDAxXzAwOCIpLCAjID0gInR3b19vcl9tb3JlX3JhY2VzIgogIAogIHllYXIgPSAyMDIyLAogIHN1cnZleSA9ICJhY3M1IiwKICBvdXRwdXQgPSAid2lkZSIKKQoKYWxsX3pjdGEgPC0gYWxsX3pjdGEgJT4lCiAgcmVuYW1lKAogICAgdG90YWxfcG9wdWxhdGlvbiA9IEIwMTAwM18wMDFFLCAgICAgIAogICAgbWVkaWFuX2FnZSA9IEIwMTAwMl8wMDFFLCAgICAgICAgICAgIAogICAgbWVkaWFuX2luY29tZSA9IEIxOTAxM18wMDFFLCAgICAgICAgCiAgICB3aGl0ZV9hbG9uZSA9IEIwMjAwMV8wMDJFLCAgICAgICAgICAKICAgIGJsYWNrX2Fsb25lID0gQjAyMDAxXzAwM0UsICAgICAgICAgCiAgICBuYXRpdmVfYW1lcmljYW4gPSBCMDIwMDFfMDA0RSwgICAgICAKICAgIGFzaWFuX2Fsb25lID0gQjAyMDAxXzAwNUUsICAgICAgICAgIAogICAgaGF3YWlpYW5fcGFjaWZpYyA9IEIwMjAwMV8wMDZFLCAgICAgCiAgICBvdGhlcl9yYWNlID0gQjAyMDAxXzAwN0UsICAgICAgICAgICAKICAgIHR3b19vcl9tb3JlX3JhY2VzID0gQjAyMDAxXzAwOEUgICAgIAogICkKCmBgYApgYGB7cn0KCnppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyA8LSB6aXBzX2Z1bGxfbWFyeWxhbmRfb25seV9kb25vcnMgfD4KICBtdXRhdGUoCiAgICBjb250cmlidXRvcl96aXAgPSBzdWJzdHIoY29udHJpYnV0b3JfemlwLCAxLCA1KSwKICAgIGNvbnRyaWJ1dG9yX3ppcCA9IHNwcmludGYoIiUwNXMiLCBjb250cmlidXRvcl96aXApCiAgKQoKemlwc19mdWxsX21hcnlsYW5kX29ubHlfZG9ub3JzIDwtIHppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyB8PgogIG11dGF0ZShjb250cmlidXRvcl96aXAgPSBhcy5jaGFyYWN0ZXIoY29udHJpYnV0b3JfemlwKSkKCm1lcmdlZF9kYXRhX21kIDwtIHppcHNfZnVsbF9tYXJ5bGFuZF9vbmx5X2Rvbm9ycyAlPiUKICBsZWZ0X2pvaW4oYWxsX3pjdGEsIGJ5ID0gYygiY29udHJpYnV0b3JfemlwIiA9ICJHRU9JRCIpKQoKc3VtbWFyaXplZF9tZF9kb25vcl9kZW1vc19ieV96aXAgPC0gbWVyZ2VkX2RhdGFfbWQgJT4lCiAgZ3JvdXBfYnkoY29udHJpYnV0b3JfemlwKSB8PgogIHN1bW1hcml6ZSgKICAgIHRvdGFsX3BvcHVsYXRpb24gPSBmaXJzdCh0b3RhbF9wb3B1bGF0aW9uKSwgIAogICAgbWVkaWFuX2FnZSA9IGZpcnN0KG1lZGlhbl9hZ2UpLCAgICAgICAgICAgICAKICAgIG1lZGlhbl9pbmNvbWUgPSBmaXJzdChtZWRpYW5faW5jb21lKSwgICAgICAgCiAgICB3aGl0ZV9hbG9uZSA9IGZpcnN0KHdoaXRlX2Fsb25lKSwgICAgICAgICAgICAKICAgIGJsYWNrX2Fsb25lID0gZmlyc3QoYmxhY2tfYWxvbmUpLCAgICAgICAgICAgCiAgICBuYXRpdmVfYW1lcmljYW4gPSBmaXJzdChuYXRpdmVfYW1lcmljYW4pLCAgIAogICAgYXNpYW5fYWxvbmUgPSBmaXJzdChhc2lhbl9hbG9uZSksICAgICAgICAgICAKICAgIGhhd2FpaWFuX3BhY2lmaWMgPSBmaXJzdChoYXdhaWlhbl9wYWNpZmljKSwgIAogICAgb3RoZXJfcmFjZSA9IGZpcnN0KG90aGVyX3JhY2UpLCAgICAgICAgICAgICAKICAgIHR3b19vcl9tb3JlX3JhY2VzID0gZmlyc3QodHdvX29yX21vcmVfcmFjZXMpLAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgICAgICAgICAgICAgICAgICAgICAgCiAgICB0b3RhbF9hbXRfZG9uYXRlZCA9IHN1bSh0b3RhbF9kb25hdGVkLCBuYS5ybSA9IFRSVUUpIAogICkgfD4gCiAgYXJyYW5nZShkZXNjKG51bV9kb25hdGlvbnMpKSAgfD4gCiAgbXV0YXRlKAogICAgd2hpdGVfYWxvbmVfcGVyX2NhcGl0YSA9IHdoaXRlX2Fsb25lIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIGJsYWNrX2Fsb25lX3Blcl9jYXBpdGEgPSBibGFja19hbG9uZSAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBuYXRpdmVfYW1lcmljYW5fcGVyX2NhcGl0YSA9IG5hdGl2ZV9hbWVyaWNhbiAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBhc2lhbl9hbG9uZV9wZXJfY2FwaXRhID0gYXNpYW5fYWxvbmUgLyB0b3RhbF9wb3B1bGF0aW9uLAogICAgaGF3YWlpYW5fcGFjaWZpY19wZXJfY2FwaXRhID0gaGF3YWlpYW5fcGFjaWZpYyAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBvdGhlcl9yYWNlX3Blcl9jYXBpdGEgPSBvdGhlcl9yYWNlIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIHR3b19vcl9tb3JlX3JhY2VzX3Blcl9jYXBpdGEgPSB0d29fb3JfbW9yZV9yYWNlcyAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBtZWRpYW5faW5jb21lX3Blcl9jYXBpdGEgPSBtZWRpYW5faW5jb21lIC8gdG90YWxfcG9wdWxhdGlvbgogICkgfD4KICAgbXV0YXRlKAogICAgd2hpdGVfYWxvbmVfcGVyY2VudCA9IHdoaXRlX2Fsb25lX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBibGFja19hbG9uZV9wZXJjZW50ID0gYmxhY2tfYWxvbmVfcGVyX2NhcGl0YSAqIDEwMCwKICAgIG5hdGl2ZV9hbWVyaWNhbl9wZXJjZW50ID0gbmF0aXZlX2FtZXJpY2FuX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBhc2lhbl9hbG9uZV9wZXJjZW50ID0gYXNpYW5fYWxvbmVfcGVyX2NhcGl0YSAqIDEwMCwKICAgIGhhd2FpaWFuX3BhY2lmaWNfcGVyY2VudCA9IGhhd2FpaWFuX3BhY2lmaWNfcGVyX2NhcGl0YSAqIDEwMCwKICAgIG90aGVyX3JhY2VfcGVyY2VudCA9IG90aGVyX3JhY2VfcGVyX2NhcGl0YSAqIDEwMCwKICAgIHR3b19vcl9tb3JlX3JhY2VzX3BlcmNlbnQgPSB0d29fb3JfbW9yZV9yYWNlc19wZXJfY2FwaXRhICogMTAwLAogICAgbWVkaWFuX2luY29tZV9wZXJfY2FwaXRhID0gbWVkaWFuX2luY29tZV9wZXJfY2FwaXRhICogMTAwCiAgKQoKYGBgCmBgYHtyfQoKY2xlYW5lcl9tZF9kb25vcl9kZW1vcyA8LSBzdW1tYXJpemVkX21kX2Rvbm9yX2RlbW9zX2J5X3ppcCB8PiAKICAgIHNlbGVjdCgtbWF0Y2hlcygiX3Blcl9jYXBpdGEiKSwgLSJuYXRpdmVfYW1lcmljYW4iLCAtImhhd2FpaWFuX3BhY2lmaWMiLCAtIm90aGVyX3JhY2UiLCAtInR3b19vcl9tb3JlX3JhY2VzIiwgLSJ3aGl0ZV9hbG9uZSIsIC0iYmxhY2tfYWxvbmUiLCAtImFzaWFuX2Fsb25lIikKCgpgYGAKI3JlcGVhdCBmb3Igb3V0IG9mIHN0YXRlIGRvbm9ycyAKCmBgYHtyfQpvdXRfb2Zfc3RhdGVfZG9ub3JzIDwtIGZpbmFsX2RhdGFfc3RhdGVzIHw+CiAgZmlsdGVyKHN0YXRlICE9ICJNRCIpCgp4bWFyeWxhbmRfZG9ub3JzIDwtIGZpbmFsX2RhdGFfc3RhdGVzIHw+CiAgZmlsdGVyKHN0YXRlID09ICJNRCIpCgpvdXRfb2Zfc3RhdGVfb25seV9kb25vcnMgPC0gb3V0X29mX3N0YXRlX2Rvbm9ycyB8PgogIGZpbHRlcighY29udHJpYnV0b3JfbmFtZSAlaW4lIHhtYXJ5bGFuZF9kb25vcnMkY29udHJpYnV0b3JfbmFtZSkgIHw+Cmdyb3VwX2J5KGNvbnRyaWJ1dG9yX25hbWUsIGNvbnRyaWJ1dG9yX3ppcCkgfD4KICBzdW1tYXJpemUoCiAgICBudW1fZG9uYXRpb25zID0gbigpLCAgICAgICAgICAgIAogICAgdG90YWxfZG9uYXRlZCA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQpICAKICApIHw+CiAgYXJyYW5nZShkZXNjKG51bV9kb25hdGlvbnMpKSAgCgoKb3V0X29mX3N0YXRlX29ubHlfZG9ub3JzIDwtIG91dF9vZl9zdGF0ZV9vbmx5X2Rvbm9ycyB8PgogIG11dGF0ZSgKICAgIGNvbnRyaWJ1dG9yX3ppcCA9IHN1YnN0cihjb250cmlidXRvcl96aXAsIDEsIDUpLAogICAgY29udHJpYnV0b3JfemlwID0gc3ByaW50ZigiJTA1cyIsIGNvbnRyaWJ1dG9yX3ppcCkKICApCgoKYGBgCmBgYHtyfQoKbWVyZ2VkX2RhdGFfb29zIDwtIG91dF9vZl9zdGF0ZV9vbmx5X2Rvbm9yc3w+CiAgbGVmdF9qb2luKGFsbF96Y3RhLCBieSA9IGMoImNvbnRyaWJ1dG9yX3ppcCIgPSAiR0VPSUQiKSkKCmNsZWFuZXJfb29zX2Rvbm9yX2RlbW9zIDwtIG1lcmdlZF9kYXRhX29vcyAgCiAgIGdyb3VwX2J5KGNvbnRyaWJ1dG9yX3ppcCkgfD4KICBzdW1tYXJpemUoCiAgICB0b3RhbF9wb3B1bGF0aW9uID0gZmlyc3QodG90YWxfcG9wdWxhdGlvbiksIAogICAgbWVkaWFuX2FnZSA9IGZpcnN0KG1lZGlhbl9hZ2UpLCAgICAgICAgICAgICAgCiAgICBtZWRpYW5faW5jb21lID0gZmlyc3QobWVkaWFuX2luY29tZSksICAgICAgICAKICAgIHdoaXRlX2Fsb25lID0gZmlyc3Qod2hpdGVfYWxvbmUpLCAgICAgICAgIAogICAgYmxhY2tfYWxvbmUgPSBmaXJzdChibGFja19hbG9uZSksICAgICAgICAgICAgCiAgICBuYXRpdmVfYW1lcmljYW4gPSBmaXJzdChuYXRpdmVfYW1lcmljYW4pLCAgIAogICAgYXNpYW5fYWxvbmUgPSBmaXJzdChhc2lhbl9hbG9uZSksICAgICAgICAgICAKICAgIGhhd2FpaWFuX3BhY2lmaWMgPSBmaXJzdChoYXdhaWlhbl9wYWNpZmljKSwKICAgIG90aGVyX3JhY2UgPSBmaXJzdChvdGhlcl9yYWNlKSwgICAgICAgICAgICAgIAogICAgdHdvX29yX21vcmVfcmFjZXMgPSBmaXJzdCh0d29fb3JfbW9yZV9yYWNlcykKICAgIG51bV9kb25hdGlvbnMgPSBuKCksICAgICAgICAgICAgICAgICAgICAgIAogICAgdG90YWxfYW10X2RvbmF0ZWQgPSBzdW0odG90YWxfZG9uYXRlZCwgbmEucm0gPSBUUlVFKSAKICApIHw+IAogIGFycmFuZ2UoZGVzYyhudW1fZG9uYXRpb25zKSkgIHw+IAogIG11dGF0ZSgKICAgCiAgICB3aGl0ZV9hbG9uZV9wZXJfY2FwaXRhID0gd2hpdGVfYWxvbmUgLyB0b3RhbF9wb3B1bGF0aW9uLAogICAgYmxhY2tfYWxvbmVfcGVyX2NhcGl0YSA9IGJsYWNrX2Fsb25lIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIG5hdGl2ZV9hbWVyaWNhbl9wZXJfY2FwaXRhID0gbmF0aXZlX2FtZXJpY2FuIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIGFzaWFuX2Fsb25lX3Blcl9jYXBpdGEgPSBhc2lhbl9hbG9uZSAvIHRvdGFsX3BvcHVsYXRpb24sCiAgICBoYXdhaWlhbl9wYWNpZmljX3Blcl9jYXBpdGEgPSBoYXdhaWlhbl9wYWNpZmljIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIG90aGVyX3JhY2VfcGVyX2NhcGl0YSA9IG90aGVyX3JhY2UgLyB0b3RhbF9wb3B1bGF0aW9uLAogICAgdHdvX29yX21vcmVfcmFjZXNfcGVyX2NhcGl0YSA9IHR3b19vcl9tb3JlX3JhY2VzIC8gdG90YWxfcG9wdWxhdGlvbiwKICAgIG1lZGlhbl9pbmNvbWVfcGVyX2NhcGl0YSA9IG1lZGlhbl9pbmNvbWUgLyB0b3RhbF9wb3B1bGF0aW9uCiAgKSB8PgogICBtdXRhdGUoCiAgCiAgICB3aGl0ZV9hbG9uZV9wZXJjZW50ID0gd2hpdGVfYWxvbmVfcGVyX2NhcGl0YSAqIDEwMCwKICAgIGJsYWNrX2Fsb25lX3BlcmNlbnQgPSBibGFja19hbG9uZV9wZXJfY2FwaXRhICogMTAwLAogICAgbmF0aXZlX2FtZXJpY2FuX3BlcmNlbnQgPSBuYXRpdmVfYW1lcmljYW5fcGVyX2NhcGl0YSAqIDEwMCwKICAgIGFzaWFuX2Fsb25lX3BlcmNlbnQgPSBhc2lhbl9hbG9uZV9wZXJfY2FwaXRhICogMTAwLAogICAgaGF3YWlpYW5fcGFjaWZpY19wZXJjZW50ID0gaGF3YWlpYW5fcGFjaWZpY19wZXJfY2FwaXRhICogMTAwLAogICAgb3RoZXJfcmFjZV9wZXJjZW50ID0gb3RoZXJfcmFjZV9wZXJfY2FwaXRhICogMTAwLAogICAgdHdvX29yX21vcmVfcmFjZXNfcGVyY2VudCA9IHR3b19vcl9tb3JlX3JhY2VzX3Blcl9jYXBpdGEgKiAxMDAsCiAgICBtZWRpYW5faW5jb21lX3Blcl9jYXBpdGEgPSBtZWRpYW5faW5jb21lX3Blcl9jYXBpdGEgKiAxMDAgIAogICkgfD4gc2VsZWN0KC1tYXRjaGVzKCJfcGVyX2NhcGl0YSIpLCAtIm5hdGl2ZV9hbWVyaWNhbiIsIC0iaGF3YWlpYW5fcGFjaWZpYyIsIC0ib3RoZXJfcmFjZSIsIC0idHdvX29yX21vcmVfcmFjZXMiLCAtIndoaXRlX2Fsb25lIiwgLSJibGFja19hbG9uZSIsIC0iYXNpYW5fYWxvbmUiKQoKCmBgYApgYGB7cn0KY2xlYW5lcl9vb3NfZG9ub3JfZGVtb3MgfD4gYXJyYW5nZShkZXNjKHRvdGFsX2FtdF9kb25hdGVkKSkKYGBgCgpgYGB7cn0KY2xlYW5lcl9tZF9kb25vcl9kZW1vcyB8PiBhcnJhbmdlKGRlc2ModG90YWxfYW10X2RvbmF0ZWQpKQpgYGAKSXQgbG9va3MgbGlrZSBpbiB0aGlzIGRhdGEsIHRoZSB0b3AgY29udHJpYnV0aW5nIHppcCBjb2RlcyBhcmUgdGhlIHNhbWUgZm9yIG91dC1vZi1zdGF0ZSBhbmQgaW4tc3RhdGUgZG9uYXRpb25zLiBUaGUgdG9wIG91dC1vZi1zdGF0ZSBkb25vcnMgZG9uYXRlZCBtdWNoIG1vcmUgdGhhbiB0aGUgaW4tc3RhdGUgZG9ub3JzLCBhbmQgdGhlcmUgd2VyZSBtb3JlIG9mIHRoZW0gaW4gdGhlIHRvcCB6aXAgY29kZXMuIEEgdG9wIGluLXN0YXRlIGRvbm9yIHppcCBjb2RlIGlzIEFuYXBvbGlzLCBhbmQgZG9lc24ndCBhcHBlYXIgaW4gdGhlIHRvcCB0ZW4gemlwIGNvZGVzIGZvciBvdXQtb2Ytc3RhdGUuCgpBcyB3ZSBrZWVwIGdvaW5nIHRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uLCB3ZSBzaG91bGQgY2FsY3VsYXRlIHRoZSBhbW91bnQgZG9uYXRlZCBwZXIgcGVyc29uLiBXZSBzaG91bGQgY29uc2lkZXIgcHJlc2VudGluZyB0aGlzIGRhdGEgd2l0aCB0d28gbWFwcyBzaWRlLWJ5LXNpZGUuIAoKCiMgUXVlc3Rpb24gNDogQW1vbmcgdG9wIE1hcnlsYW5kIGRvbm9ycywgd2hhdCBwcm9mZXNzaW9ucyBkb25hdGUgdGhlIG1vc3QgbW9uZXkgdG8gc2VuYXRlIGNhbXBhaWducz8gCgpgYGB7cn0KaW5kaXZpZHVhbF9kb25vcnMgIDwtIGZpbmFsX2RhdGFfc3RhdGVzfD4KZ3JvdXBfYnkoY29udHJpYnV0b3JfbmFtZSwgY29udHJpYnV0b3JfemlwLCBjb250cmlidXRvcl9vY2N1cGF0aW9uLCBjb250cmlidXRvcl9lbXBsb3llcikgfD4gCiAgc3VtbWFyaXplKAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgICAgICAgICAgICAgICAKICAgIHRvdGFsX2RvbmF0ZWQgPSBzdW0oY29udHJpYnV0aW9uX3JlY2VpcHRfYW1vdW50KSAKICApIHw+CiAgYXJyYW5nZShkZXNjKG51bV9kb25hdGlvbnMpKSAgCgpqb2JzX3RvX2NsZWFuIDwtIGluZGl2aWR1YWxfZG9ub3JzIHw+IAogIGdyb3VwX2J5KGNvbnRyaWJ1dG9yX29jY3VwYXRpb24pIHw+CiAgc3VtbWFyaXplKAogICAgbnVtYmVyX2pvYnMgPSBuKCksCiAgICBudW1fZG9uYXRpb25zID0gbigpLCAgCiAgICAgdG90YWxfZG9uYXRpb25zID0gc3VtKHRvdGFsX2RvbmF0ZWQpCiAgKSB8PiBhcnJhbmdlKGRlc2MobnVtYmVyX2pvYnMpKSB8Pgp3cml0ZV9jc3YoImpvYnNfdG9fY2xlYW4uY3N2IikKCmBgYAoKYGBge3J9CmJvc3NfdG9fY2xlYW4gIDwtIGluZGl2aWR1YWxfZG9ub3JzIHw+IAogIGdyb3VwX2J5KGNvbnRyaWJ1dG9yX2VtcGxveWVyKSB8PgogIHN1bW1hcml6ZSgKICAgIG51bWJlcl9qb2JzID0gbigpLAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgIAogICAgIHRvdGFsX2RvbmF0aW9ucyA9IHN1bSh0b3RhbF9kb25hdGVkKQogICkgfD4gYXJyYW5nZShkZXNjKG51bWJlcl9qb2JzKSkgfD4Kd3JpdGVfY3N2KCJib3NzX3RvX2NsZWFuLmNzdiIpCmBgYAoKCmBgYHtyfQpjbGVhbl9lbXBsb3llciA8LSByZWFkX2NzdigiY2xlYW5lZF9ib3NzLmNzdiIpCmNsZWFuX29jY3VwYXRpb24gPC0gcmVhZF9jc3YoImNsZWFuX2pvYnMuY3N2IikKCmNsZWFuX29jY3VwYXRpb24gfD4gCiAgZ3JvdXBfYnkoY2xlYW5lZF9qam9icykgIHw+IAogIHN1bW1hcml6ZSgKICAgIG51bWJlcl9qb2JzID0gbigpLAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgIAogICAgIHRvdGFsX2RvbmF0ZWQgPSBzdW0odG90YWxfZG9uYXRpb25zKSkgfD4gCiAgYXJyYW5nZShkZXNjKG51bWJlcl9qb2JzKSkKCmBgYApgYGB7cn0KY2xlYW5fb2NjdXBhdGlvbiB8PiAKICBncm91cF9ieShjbGVhbmVkX2pqb2JzKSAgfD4gCiAgc3VtbWFyaXplKAogICAgbnVtYmVyX2pvYnMgPSBuKCksCiAgICBudW1fZG9uYXRpb25zID0gbigpLCAgCiAgICAgdG90YWxfZG9uYXRlZCA9IHN1bSh0b3RhbF9kb25hdGlvbnMpKSB8PiAKICBhcnJhbmdlKGRlc2ModG90YWxfZG9uYXRlZCkpCgpgYGAKCgpgYGB7cn0KY2xlYW5fZW1wbG95ZXIgfD4gCiAgZ3JvdXBfYnkoY2xlYW5lZF9ib3NzKSB8PgogIHN1bW1hcml6ZSgKICAgIG51bWJlcl9qb2JzID0gbigpLAogICAgbnVtX2RvbmF0aW9ucyA9IG4oKSwgIAogICAgIHRvdGFsX2RvbmF0ZWQgPSBzdW0odG90YWxfZG9uYXRpb25zKQogICkgfD4KICBhcnJhbmdlKGRlc2MobnVtYmVyX2pvYnMpKQpgYGAKCgpUbyBhbnN3ZXIgdGhpcyBxdWVzdGlvbiwgSSBwdXQgYSBjc3Ygb2Ygb2NjdXBhdGlvbnMgaW50byBPcGVuUmVmaW5lLiBXZSBvcmlnaW5hbGx5IHBsYW5uZWQgdG8gbGltaXQgdGhlIGRhdGEgc2V0LCBidXQgSSBmb3VuZCB0aGF0IHRoZXJlIHdlcmUgb25seSAxLDk2MyBqb2IgdGl0bGVzIGluIHRoZSBkYXRhIHNldCwgd2hpY2ggc2VlbWVkIGxpa2UgYSByZWFzb25hYmxlIG51bWJlciB0byByZWZpbmUgZG93biBpbnRvIGEgc21hbGxlciBsaXN0IG9mIGpvYnMuIAoKVG8gZG8gdGhpcywgSSBncm91cGVkIGNlcnRhaW4gam9icyBpbnRvIGNhdGVnb3JpZXMgbGlrZSAiZGlyZWN0b3IiIGFuZCAiZXhlY3V0aXZlIiAtLSBzbyBqb2IgdGl0bGVzIGxpa2UgInNhbGVzIGRpcmVjdG9yIiB3ZW50IGludG8gdGhlICJkaXJlY3RvciIgY2F0ZWdvcnkuIAoKSSB0aGluayB0aGUgT3BlblJlZmluZSBtZXRob2QgbWF5IGJlIGZsYXdlZC9ub3QgdGVsbCB1cyB2ZXJ5IG11Y2ggYmVjYXVzZSBJIHdhcyBvbmx5IGFibGUgdG8gZ2V0IHRoZSBsaXN0IGRvd24gdG8gMSw0NjQgam9icyBhZnRlciBhYm91dCAxIGhvdXIgb2YgcmVmaW5pbmcuIEkgYWxzbyB3b3JyeSB0aGF0IHRoZSBjYXRlZ29yaXppbmcgbWV0aG9kIEkgdXNlZCBvYnN1cmVzIGluZm9ybWF0aW9uIHRoYXQgbWlnaHQgYmUgaW1wb3J0YW50IC0tIERpcmVjdG9yIG9mIFNhbGVzIGlzIGRpZmZlcmVudCBmcm9tIERpcmVjdG9yIG9mIEVkdWNhdGlvbiwgZm9yIGV4YW1wbGUsIGFuZCB0aGVzZSBqb2JzIHdlcmUgZ3JvdXBlZCBpbnRvIHRoZSBzYW1lIHBvc2l0aW9uLiBBbmQgdGhlcmUgYXJlIHN1Y2ggdmFyaWF0aW9ucyBpbiBqb2IgdGl0bGUgdGhhdCBJIHdhcyBub3QgYWJsZSB0byBjYXRlZ29yaXplIGFsbCBqb2IgdGl0bGVzIHdlbGwuIAoKV2hhdCB3ZSBjYW4gdGVsbCBmcm9tIHRoaXMgZGF0YSBpcyB0aGF0IHRoZSBtb3N0IGZyZXF1ZW50bHktaGVsZCBqb2JzIGFtb25nIE1hcnlsYW5kIGRvbm9ycyBhcmUgbGVhZGVyc2hpcC9tYW5hZ2VtZW50IHBvc2l0aW9ucywgc28gbGlrZWx5IHdlbGwtcGFpZC4gVGhlIG1vc3QgbW9uZXkgd2FzIGRvbmF0ZWQgYnkgcGVvcGxlIHdobyBhcmUgbm90IGVtcGxveWVkIG9yIHJldGlyZWQuIAoKV2Ugc2hvdWxkIGNvbnNpZGVyIHJlcGVhdGluZyB0aGlzIHF1ZXN0aW9uIHdpdGggaW5mb3JtYXRpb24gYWJvdXQgZW1wbG95ZXJzLiBJJ3ZlIHN0YXJ0ZWQgdG8gZG8gdGhpcyBpbiBPcGVuUmVmaW5lLCBidXQgdGhpcyB3aWxsIHRha2UgbG9uZ2VyLCBiZWNhdXNlIHRoZXJlIGFyZSBtb3JlIHRoYW4gNywwMDAgZGlmZmVyZW50IGVtcGxveWVycy4gSG93ZXZlciwgbWFueSBvZiB0aGVzZSBzZWVtIHRvIGJlIGFibGUgdG8gYmUgcmVjb25jaWxlZCBpbiBPcGVuUmVmaW5lLiBBbmQgSSdtIGludGVyZXN0ZWQgdG8gc2VlIGhvdyBtYW55IGRvbmF0aW9ucy9ob3cgbXVjaCBtb25leSB3YXMgZG9uYXRlZCBieSBwZW9wbGUgd2hvIHdvcmsgZm9yIHRvcCBlbXBsb3llcnMgaW4gTWFyeWxhbmQsIGxpa2UgSm9obnMgSG9wa2lucy4gCgoKI1F1ZXN0aW9uIDU6IFdoYXTigJlzIHRoZSBtYWtldXAgb2YgZG9uYXRpb25zIHJlY2VpdmVkIGJ5IEhvZ2FuIGFuZCBBbHNvYnJvb2tzPyBXaGF0IHBlcmNlbnRhZ2Ugb2YgdGhlaXIgb3ZlcmFsbCBkb25hdGlvbnMgd2VyZSBsYXJnZSBhbW91bnRzIG9mIG1vbmV5ICh0byBiZSBkZWZpbmVkLCBidXQgPiAkMTAwMCwgZm9yIGV4YW1wbGUpIHZzIHNtYWxsIGFtb3VudHM/CmBgYHtyfQpmaWx0ZXJlZF9kYXRhIDwtIGZpbmFsX2RhdGEgfD4KICBmaWx0ZXIoY29tbWl0dGVlX25hbWUgJWluJSBjKCJIT0dBTiBGT1IgTUFSWUxBTkQgSU5DLiIsICJBTFNPQlJPT0tTIEZPUiBTRU5BVEUiKSkKYGBgIApgYGB7cn0KbGFyZ2VfZG9uYXRpb25fdGhyZXNob2xkIDwtIDEwMDAKYGBgIAoKYGBge3J9CmhvZ2FuX2Fsc29icm9va3NfZmlsdGVyZWRfZGF0YSA8LSBmaWx0ZXJlZF9kYXRhIHw+IG11dGF0ZSggZG9uYXRpb25fY2F0ZWdvcnkgPSBpZmVsc2UoY29udHJpYnV0aW9uX3JlY2VpcHRfYW1vdW50ID4gbGFyZ2VfZG9uYXRpb25fdGhyZXNob2xkLCAiTGFyZ2UiLCAiU21hbGwiKSApCmBgYCAKYGBge3J9CmNhdGVnb3J5X3RvdGFscyA8LSBob2dhbl9hbHNvYnJvb2tzX2ZpbHRlcmVkX2RhdGEgfD4KZ3JvdXBfYnkoZG9uYXRpb25fY2F0ZWdvcnkpICU+JSBzdW1tYXJpc2UoIHRvdGFsX2Ftb3VudCA9IHN1bShjb250cmlidXRpb25fcmVjZWlwdF9hbW91bnQsIG5hLnJtID0gVFJVRSkgKQpgYGAgCmBgYHtyfQpjYXRlZ29yeV90b3RhbHMgPC0gY2F0ZWdvcnlfdG90YWxzICU+JSBtdXRhdGUoIHBlcmNlbnRhZ2UgPSB0b3RhbF9hbW91bnQgLyBzdW0odG90YWxfYW1vdW50KSAqIDEwMCApCmBgYCAKClRvIAo=